Skip to content

feat: implement invite link generation and deep-link onboarding (#16)#19

Merged
Queenode merged 2 commits into
Kolo-Org:mainfrom
adetomiwa21:feat/issue-16-invite-member-deep-linking
Jul 23, 2026
Merged

feat: implement invite link generation and deep-link onboarding (#16)#19
Queenode merged 2 commits into
Kolo-Org:mainfrom
adetomiwa21:feat/issue-16-invite-member-deep-linking

Conversation

@adetomiwa21

@adetomiwa21 adetomiwa21 commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Closes #16

Add "Invite Member" link generation and the deep-link onboarding router.

Invite links & sharing:

  • InviteButton generates a unique short URL (/invite/) for a group
  • Web Share API on mobile, plus WhatsApp deep link and copy-to-clipboard
  • groupsService mock (localStorage) creates/resolves invite codes

Deep-link onboarding router:

  • /invite/[code] async page (Next 16 awaited params) -> InviteConfirm
  • unauthenticated visitors are routed through signup first, then returned to the group Join confirmation via ?redirect=, preserving context
  • register/login honor ?redirect= with an open-redirect guard

Supporting mocks (swappable at TODO seams; no backend/auth exists yet):

  • minimal localStorage auth via useSyncExternalStore (SSR-safe)
  • seeded demo groups on the /groups dashboard

Verified: lint, tsc --noEmit, prettier --check, and next build all pass.

Summary by CodeRabbit

  • New Features
    • Added Suspense-enabled login and registration forms with safe redirect handling.
    • Introduced an authentication context with persisted login state and cross-tab updates.
    • Replaced the Groups page with a working dashboard that lists groups and shows group details.
    • Added group invite creation with copy-to-clipboard, native sharing, and WhatsApp sharing.
    • Added an invite confirmation/join flow with loading, invalid-invite handling, and join success/error states.

…-Org#16)

Add "Invite Member" link generation and the deep-link onboarding router.

Invite links & sharing:
- InviteButton generates a unique short URL (/invite/<code>) for a group
- Web Share API on mobile, plus WhatsApp deep link and copy-to-clipboard
- groupsService mock (localStorage) creates/resolves invite codes

Deep-link onboarding router:
- /invite/[code] async page (Next 16 awaited params) -> InviteConfirm
- unauthenticated visitors are routed through signup first, then returned
  to the group Join confirmation via ?redirect=, preserving context
- register/login honor ?redirect= with an open-redirect guard

Supporting mocks (swappable at TODO seams; no backend/auth exists yet):
- minimal localStorage auth via useSyncExternalStore (SSR-safe)
- seeded demo groups on the /groups dashboard

Verified: lint, tsc --noEmit, prettier --check, and next build all pass.
@vercel

vercel Bot commented Jul 19, 2026

Copy link
Copy Markdown

@adetomiwa21 is attempting to deploy a commit to the omodunni241-4260's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 16f1f6d1-6af4-4a5e-82b8-57b816c28b53

📥 Commits

Reviewing files that changed from the base of the PR and between 7218ec5 and d7b0f10.

📒 Files selected for processing (3)
  • src/components/auth/AuthForm/AuthForm.tsx
  • src/components/groups/InviteConfirm/InviteConfirm.tsx
  • src/context/AuthContext.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/components/groups/InviteConfirm/InviteConfirm.tsx
  • src/context/AuthContext.tsx
  • src/components/auth/AuthForm/AuthForm.tsx

📝 Walkthrough

Walkthrough

The change replaces placeholder authentication and groups pages with localStorage-backed auth, seeded group and invite services, reusable authentication forms, group invitation sharing, and invite deep-link onboarding with authenticated group joining.

Changes

Authentication and group invite flow

Layer / File(s) Summary
Group and invite contracts and storage
src/types/group.ts, src/types/invite.ts, src/utils/invite.ts, src/services/api/groups.ts
Defines group and invite types, generates invite and sharing URLs, and persists invite mappings and memberships through the groups service.
Hydration-safe authentication state
src/context/AuthContext.tsx, src/hooks/useAuth.ts, src/app/layout.tsx
Adds typed localStorage-backed authentication with hydration-safe external-store updates, login/logout operations, and a root provider.
Login and registration entry points
src/components/auth/AuthForm/*, src/app/(auth)/login/page.tsx, src/app/(auth)/register/page.tsx
Adds a shared login/register form with redirect sanitization and wraps both auth pages in Suspense.
Dashboard invitations and deep-link onboarding
src/app/(dashboard)/groups/page.tsx, src/components/groups/InviteButton/*, src/components/groups/InviteConfirm/*, src/app/invite/[code]/page.tsx
Displays groups with invite controls, supports copying and sharing generated links, redirects unauthenticated invite visitors through registration, and confirms group membership for authenticated visitors.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant GroupDashboard
  participant InviteButton
  participant InviteConfirm
  participant groupsService
  participant AuthForm
  GroupDashboard->>InviteButton: generate invite
  InviteButton->>groupsService: createInvite(group.id)
  InviteButton-->>InviteConfirm: share /invite/code
  InviteConfirm->>groupsService: getInvite(code)
  InviteConfirm->>AuthForm: redirect unauthenticated visitor with redirect
  AuthForm-->>InviteConfirm: return authenticated visitor to invite route
  InviteConfirm->>groupsService: joinGroup(group.id)
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.71% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the invite link generation and deep-link onboarding changes in this PR.
Linked Issues check ✅ Passed The PR implements short invite links, sharing options, and signup-to-confirmation deep linking required by #16.
Out of Scope Changes check ✅ Passed The changes stay focused on invite generation, invite onboarding, and supporting auth/dashboard plumbing.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
src/context/AuthContext.tsx (1)

60-60: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

clientSnapshot.isLoading is always false — the || clientSnapshot.isLoading check is dead code.

clientSnapshot is initialized with isLoading: false (line 50) and the only assignment (line 68) also sets isLoading: false. This means the || clientSnapshot.isLoading branch never triggers, making it misleading. If the intent was to start with isLoading: true and transition on first read, the initial value on line 50 should be true; otherwise, simplify the condition to just raw !== cachedRaw.

♻️ Simplify the condition
-  if (raw !== cachedRaw || clientSnapshot.isLoading) {
+  if (raw !== cachedRaw) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/context/AuthContext.tsx` at line 60, Remove the dead
clientSnapshot.isLoading check from the condition in AuthContext, leaving the
cache update path driven solely by raw !== cachedRaw; keep the existing
isLoading: false initialization and assignment unchanged.
src/components/groups/InviteConfirm/InviteConfirm.tsx (1)

49-54: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Redirect target embeds code unescaped in the query string.

/register?redirect=/invite/${code} isn't passed through encodeURIComponent; a code containing &/=/# would corrupt the query string and break the round-trip back to this invite (mitigated only incidentally by safeRedirect's fallback to /dashboard).

♻️ Proposed fix
-        router.replace(`/register?redirect=/invite/${code}`);
+        router.replace(`/register?redirect=${encodeURIComponent(`/invite/${code}`)}`);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/groups/InviteConfirm/InviteConfirm.tsx` around lines 49 - 54,
Update the unauthenticated redirect in InviteConfirm to encode the invite code
with encodeURIComponent before embedding it in the /register redirect query
parameter. Preserve the existing return-to-invite behavior and router.replace
flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/auth/AuthForm/AuthForm.tsx`:
- Around line 114-118: Update safeRedirect to reject redirect values beginning
with "/\\" in addition to protocol-relative "//" paths, ensuring
backslash-prefixed paths return null while valid same-site relative redirects
remain accepted.
- Line 3: Update the safeRedirect validation in AuthForm so redirect values
containing backslashes are rejected before router.replace, while preserving
valid same-origin single-slash paths and rejecting protocol-relative URLs.

In `@src/components/groups/InviteConfirm/InviteConfirm.tsx`:
- Around line 64-70: Update handleJoin to wrap groupsService.joinGroup in error
handling and always reset joining state when the request settles. Keep the
joined phase transition only on success, and preserve the existing behavior for
a missing group.
- Around line 39-62: Handle promise rejection in the invite-loading effect
around groupsService.getInvite and update phase to the existing error state when
retrieval fails, while respecting the active guard so stale requests cannot
update state after cleanup. Preserve the current success, invalid-invite,
authentication redirect, and confirmation flows.

---

Nitpick comments:
In `@src/components/groups/InviteConfirm/InviteConfirm.tsx`:
- Around line 49-54: Update the unauthenticated redirect in InviteConfirm to
encode the invite code with encodeURIComponent before embedding it in the
/register redirect query parameter. Preserve the existing return-to-invite
behavior and router.replace flow.

In `@src/context/AuthContext.tsx`:
- Line 60: Remove the dead clientSnapshot.isLoading check from the condition in
AuthContext, leaving the cache update path driven solely by raw !== cachedRaw;
keep the existing isLoading: false initialization and assignment unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 803eaa8d-11bd-48a9-bccf-23cade3a50e1

📥 Commits

Reviewing files that changed from the base of the PR and between e36aca2 and 7218ec5.

📒 Files selected for processing (17)
  • src/app/(auth)/login/page.tsx
  • src/app/(auth)/register/page.tsx
  • src/app/(dashboard)/groups/page.tsx
  • src/app/invite/[code]/page.tsx
  • src/app/layout.tsx
  • src/components/auth/AuthForm/AuthForm.tsx
  • src/components/auth/AuthForm/index.ts
  • src/components/groups/InviteButton/InviteButton.tsx
  • src/components/groups/InviteButton/index.ts
  • src/components/groups/InviteConfirm/InviteConfirm.tsx
  • src/components/groups/InviteConfirm/index.ts
  • src/context/AuthContext.tsx
  • src/hooks/useAuth.ts
  • src/services/api/groups.ts
  • src/types/group.ts
  • src/types/invite.ts
  • src/utils/invite.ts

Comment thread src/components/auth/AuthForm/AuthForm.tsx
Comment thread src/components/auth/AuthForm/AuthForm.tsx
Comment thread src/components/groups/InviteConfirm/InviteConfirm.tsx Outdated
Comment thread src/components/groups/InviteConfirm/InviteConfirm.tsx
@Queenode

Copy link
Copy Markdown
Contributor

@adetomiwa21 please fix the review coderabbitai i dropped, you can use the Prompt for all review comments with AI agents given to make it seamless

@adetomiwa21

Copy link
Copy Markdown
Contributor Author

i will work on it now, thanks for the feedback

- reject any backslash in safeRedirect (open-redirect hardening)
- handle invite resolution failure instead of infinite loading
- add try/catch/finally + error feedback to group join
- encode invite code in signup redirect round-trip
- drop dead clientSnapshot.isLoading branch in AuthContext
@adetomiwa21

Copy link
Copy Markdown
Contributor Author

@Queenode kindly review the changes has been made

@Queenode

Copy link
Copy Markdown
Contributor

Thank you for your contribution @adetomiwa21 .

@Queenode
Queenode merged commit 0a1f767 into Kolo-Org:main Jul 23, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] Implement "Invite Member" Link Generation and Deep Linking

2 participants